﻿/*
 * TrackerWebResponse
 * 
 * Author: snowdreamist
 * Date: 2011/11/18
 * 
 */

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using BitTorrentSharp.BEncoding;
using BitTorrentSharp.Peers;
using BitTorrentSharp.Helpers;

namespace BitTorrentSharp.Tracker
{
    public class TrackerWebResponse
    {
        #region consts
        /*
         * keys
         */
        internal const string FailureReasonKey = "failure reason";
        internal const string WarningMessageKey = "warning message";
        internal const string IntervalKey = "interval";
        internal const string MinIntervalKey = "min interval";
        internal const string TrackerIDKey = "tracker";
        internal const string CompleteKey = "complete";
        internal const string InCompleteKey = "incomplete";
        internal const string PeersKey = "peers";

        #endregion
        /// <summary>
        /// create an empty tracker web response
        /// </summary>
        public TrackerWebResponse() { }
        /// <summary>
        /// create tracker web response from http web response
        /// </summary>
        /// <param name="response"></param>
        public TrackerWebResponse(HttpWebResponse response)
        {
            if (response == null)
                throw new ArgumentNullException("response");
            this.ParseResponse(response);
        }
        /// <summary>
        /// failure reason
        /// </summary>
        public string FailureReason { get; internal set; }
        /// <summary>
        /// warning message
        /// </summary>
        public string WarningMessage { get; internal set; }
        /// <summary>
        /// The interval to report to tracker
        /// </summary>
        public TimeSpan Interval { get; internal set; }
        /// <summary>
        /// min interval
        /// </summary>
        public TimeSpan MinInterval { get; internal set; }
        /// <summary>
        /// Current peers
        /// </summary>
        public List<Peer> Peers { get; private set; }
        /// <summary>
        /// tracker id
        /// </summary>
        public byte[] TackerID { get; internal set; }
        /// <summary>
        /// complete peers number
        /// </summary>
        public long CompleteNumber { get; internal set; }
        /// <summary>
        /// incomplete peers number
        /// </summary>
        public long IncompleteNumber { get; internal set; }

        #region private

        private void ParseResponse(HttpWebResponse response)
        {
            BValue rootObject = null;

            // deserialize
            using(var respStream = response.GetResponseStream())
            using(var preparedStream = respStream.PreparedStream())
                rootObject = BValue.Deserialize(preparedStream);
            // check root type
            if (!(rootObject is BDictionary))
                throw new ArgumentException("Invalid root object type");
            // root
            var responseDict = (BDictionary)rootObject;

            // interval [Required]
            if (!responseDict.ContainsKey(TrackerWebResponse.IntervalKey))
                throw new ArgumentException("Interval not found");
            this.Interval = TimeSpan.FromSeconds((BNumber)responseDict[TrackerWebResponse.IntervalKey]);
            
            // check failurereason
            if (responseDict.ContainsKey(TrackerWebResponse.FailureReasonKey))
            {
                // failed
                this.FailureReason = ((BString)responseDict[TrackerWebResponse.FailureReasonKey]).ToString(Encoding.UTF8);
            }
            else
            {
                // succeed
                // warning message [Optional]
                if (responseDict.ContainsKey(TrackerWebResponse.WarningMessageKey))
                    this.WarningMessage = ((BString)responseDict[TrackerWebResponse.WarningMessageKey]).ToString(Encoding.UTF8);
                // min interval [Optional]
                if (responseDict.ContainsKey(TrackerWebResponse.MinIntervalKey))
                    this.MinInterval = TimeSpan.FromSeconds((double)(((BNumber)responseDict[TrackerWebResponse.MinIntervalKey]).Value));
                else
                    this.MinInterval = TimeSpan.Zero;
                // peers [Required]
                if (!responseDict.ContainsKey(TrackerWebResponse.PeersKey))
                    throw new ArgumentException("Peers not found");

                if (responseDict[TrackerWebResponse.PeersKey] is BList)
                {
                    // no compact
                    this.Peers = ((BList)responseDict[TrackerWebResponse.PeersKey])
                        .Cast<BDictionary>()
                        .Select(p => TrackerHelper.CreatePeerWithNoCompactInput(p))
                        .ToList();
                }
                else
                {
                    // compact
                    this.Peers = TrackerHelper.CreatePeersWithCompactInput((BString)responseDict[TrackerWebResponse.PeersKey]).ToList();
                }
                // tracker ID [Optional]
                if (responseDict.ContainsKey(TrackerWebResponse.TrackerIDKey))
                    this.TackerID = ((BString)responseDict[TrackerWebResponse.TrackerIDKey]).Content;
                // complete number
                if (responseDict.ContainsKey(TrackerWebResponse.CompleteKey))
                    this.CompleteNumber = (BNumber)responseDict[TrackerWebResponse.CompleteKey];
                // incomplete number
                if (responseDict.ContainsKey(TrackerWebResponse.InCompleteKey))
                    this.IncompleteNumber = (BNumber)responseDict[TrackerWebResponse.InCompleteKey];
            }
        }

        #endregion
    }
}
